// $codepro.audit.disable
/**
 * package for all the model classes to fit the mvc format
 */
package javamonsters.model;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;

/**
 * Marketplace handles trading between the game and the player.
 * @author jham9
 * @version 1.0
 */
public class Marketplace implements java.io.Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 5213288585079687782L;
	
	/**
	 * object output stream
	 */
	private ObjectOutputStream out;
	
	/**
	 * list of trade goods
	 */
	private final List<TradeGood> tradeGoodList;
	
	/**
	 * map of goods the player can buy
	 */
	private final Map<TradeGood,InventoryItem> buyMap;

	/**
	 * map of goods the player can sell
	 */
	private Map<TradeGood,InventoryItem> sellMap;
	
	/**
	 * the parent planet of the Marketplace
	 */
	private final Planet parentPlanet;
	
	/**
	 * the current character
	 */
	private Character currCharacter;
	
	/**
	 * associated ship for the character
	 */
	private Ship associatedShip;

	/**
	 * @param parentPlanet the parent planet of the Marketplace
	 */
	public Marketplace(Planet parentPlanet) {

		this.parentPlanet = parentPlanet;
		//this should be the Player's ship from Player.getShip()
		this.associatedShip = null;
		buyMap = new HashMap<TradeGood,InventoryItem>();
		sellMap = new HashMap<TradeGood,InventoryItem>();
		tradeGoodList = new ArrayList<TradeGood>(Arrays.asList(TradeGood.values()));
	}
	
	/**
	 * @return String representation of marketplace
	 */
	public String toString() {
		return "marketplace";
	}

	/**
	 * Takes a good's base price and determines a final price
	 * to display in the marketplace
	 * @param good TradeGood for which the price should be calculated
	 * @return final adjusted price
	 */
	public BigDecimal calculatePrice(TradeGood good) {
		final Random rand = new Random();
		final Integer varInt = rand.nextInt(good.getVariance().intValue());
		final BigDecimal var = new BigDecimal(varInt);
		BigDecimal finalPrice = new BigDecimal(0.0);
		finalPrice = finalPrice.add(good.getBasePrice());

		final BigDecimal ipl = good.getIncPerLvl();
		final BigDecimal techLvl = new BigDecimal(parentPlanet.getTechLevel().ordinal());
		final BigDecimal mtlp = new BigDecimal(good.getMinProdTechLvl().ordinal());
		finalPrice = finalPrice.add(ipl.multiply(techLvl.subtract(mtlp)));
		
		final int fiftyFifty = 2;
		final int oneHundred = 100;
		
		if(rand.nextInt(fiftyFifty) == 0) {
			finalPrice = finalPrice.add(good.getBasePrice().multiply(
		    var.divide(new BigDecimal(oneHundred))));
		} else {
			finalPrice = finalPrice.subtract(good.getBasePrice().multiply(
					     var.divide(new BigDecimal(oneHundred))));
		}
		
		final double fiftyPercent = 0.5;
		
		if(parentPlanet.getAttributes().compareTo(good.getCheapCond()) == 0) {
			finalPrice = finalPrice.subtract(finalPrice.multiply
					(new BigDecimal(fiftyPercent)));
		} else if (parentPlanet.getAttributes().compareTo(good.getXpnsvCond()) == 0) {
			finalPrice = finalPrice.add(finalPrice.multiply
					(new BigDecimal(fiftyPercent)));
		}

		return finalPrice;

		//30 (the base price) + 3*2 (the IPL * (Planet Tech Level - MTLP)) + (variance)
	}

	/**
	 * Places what marketplace and player can sell to each other in hash maps
	 */
	public void initializeMaps() {
		buyMap.clear();
		sellMap.clear();
		
		/*
		 * loop through this, see what can be sold to player on this 
		 * particular planet, make a new array list
		 */
		
		final int abundant = 20;
		final int notAbundant = 10;
		
		for(TradeGood tg : tradeGoodList) {
			if (parentPlanet.getTechLevel().compareTo(tg.getMinProdTechLvl()) >= 0) {
				if (parentPlanet.getTechLevel().equals(tg.getTopProdTechLvl())) {
					buyMap.put(tg, new InventoryItem(calculatePrice(tg), abundant));
				} else {
					buyMap.put(tg, new InventoryItem(calculatePrice(tg), notAbundant));
				}
			} 
		}

		/*
		 * Now gathering what can the Player sell to the MarketPlace
		 */
		for(TradeGood tg : tradeGoodList) {
			Map<TradeGood,Integer> cargo = associatedShip.getCargoContents();
			if(cargo.containsKey(tg)) {
				sellMap.put(tg, new InventoryItem(calculatePrice(tg), cargo.get(tg))); 
			}
		}
	}

	/**
	 * Player wants to buy something from the Marketplace
	 * @param good TradeGood that wants to be purchased
	 * @param quantity how many goods to buy
	 */
	//TODO: implement buying more than 1 TradeGood at a time
	public void buy(TradeGood good, int quantity) {
		final InventoryItem item = buyMap.get(good);
		final BigDecimal priceOfGood = item.getPrice();
		final BigDecimal totalPrice = priceOfGood.multiply(new BigDecimal(quantity));
		Integer currCargoSize = 0;

		final Set<Entry<TradeGood, Integer>> cargo =
			  associatedShip.getCargoContents().entrySet();
		
		for(Entry<TradeGood, Integer> e : cargo) {
			currCargoSize += e.getValue();
		}
		
		final int creditCompare = currCharacter.getCredits().compareTo(totalPrice);
		final int maxCargo = associatedShip.getMaxCargo();

		/*
		 * Seeing if the Player already has this good in the hull
		 */
		if(sellMap.get(good) != null) {
			/*
			 * check if the player has enough money and space in the hull
			 */
			if(creditCompare >= 1 && ((currCargoSize + quantity) <= maxCargo)) {
				
				currCharacter.removeCredits(totalPrice);
	
				associatedShip.addCargo(good, quantity);
	
				final InventoryItem buyItem = buyMap.get(good);
				final InventoryItem sellItem = sellMap.get(good);
				final int oldBuyQuantity = buyItem.getQuantity();
				final int oldSellQuantity = sellItem.getQuantity();
				
				/*
				 * updating the quantities here
				 */
				buyMap.get(good).setQuantity(oldBuyQuantity - quantity);
				sellMap.get(good).setQuantity(oldSellQuantity + quantity);
				
			}
			
		}
		/* Player does not have an item like this in their inventory */
		else {
			if(creditCompare >= 1 && ((currCargoSize + quantity) <= maxCargo)) {
				currCharacter.removeCredits(totalPrice);
				sellMap.put(good, new InventoryItem(priceOfGood.divide(
						new BigDecimal(2)), 1));
				associatedShip.addCargo(good, 1);
				final InventoryItem buyItem = buyMap.get(good);
				final int oldBuyQuantity = buyItem.getQuantity();
				buyMap.get(good).setQuantity(oldBuyQuantity - quantity);
				//System.out.println(currCharacter.getCredits());
				
			}
			
		}
	}

	/**
	 * Player wants to sell something to the Marketplace
	 * @param good good to be sold by the player
	 * @param quantity how many player wants to sell of good
	 */
	public void sell(TradeGood good, Integer quantity) {

		final InventoryItem sellItem = sellMap.get(good);
		if (sellItem == null) {
			return;
		}
		final BigDecimal priceOfGood = sellItem.getPrice();
		
		/*
		 * item does not exist in the Marketplace already, so
		 * make a new entry in the hash map
		 */
		if(buyMap.get(good) == null) {
			buyMap.put(good, new InventoryItem(priceOfGood.multiply(
					   new BigDecimal(2)), 1));
		}
		else {
			final InventoryItem buyItem = buyMap.get(good);
			final int mktOldQuantity = buyItem.getQuantity();
			buyMap.get(good).setQuantity(mktOldQuantity + 1);
		}
		
		/*
		 * start removing cargo quantity now
		 */
		
		final int characterOldQuantity = sellItem.getQuantity();
		
		/*
		 * if the selling will make quantity = 0, remove it from the hashmap and cargo
		 */
		if(characterOldQuantity == 1) {
			sellMap.remove(good);
			associatedShip.getCargoContents().remove(good);
		}
		else {
			sellMap.get(good).setQuantity(characterOldQuantity - 1);
		}
		associatedShip.removeCargo(good, 1);
		currCharacter.addCredits(priceOfGood);
	}

	/**
	 * Setter for ship so Marketplace can examine hull for buying/selling items
	 * @param associatedShip player's Ship
	 */
	public void setAssociatedShip(Ship associatedShip) {
		this.associatedShip = associatedShip;
	}

	/**
	 * Getter for Marketplace's map of goods
	 * @return hashmap of goods and InventoryItems
	 */
	public Map<TradeGood, InventoryItem> getBuyMap() {
		return buyMap;
	}

	/**
	 * Getter for Player's map of goods
	 * @return hashmap of goods and InventoryItems
	 */
	public Map<TradeGood, InventoryItem> getSellMap() {
		return sellMap;
	}
	
	/**
	 * Sets the Character object
	 * @param currCharacter what to use to set the object
	 */
	public void setCurrentCharacter(Character currCharacter) {
		this.currCharacter = currCharacter;
	}
	
	/**
	 * Get the Character from Marketplace
	 * @return the Character object
	 */
	public Character getCharacter() {
		return currCharacter;
	}
	
	/**
	 * Get the Ship object from Marketplace
	 * @return the Ship object
	 */
	public Ship getAssociatedShip() {
		return associatedShip;
	}
	
	/**
	 * Get the Planet object from Marketplace
	 * @return the Planet object
	 */
	public Planet getParentPlanet() {
		return parentPlanet;
	}
	
	/**
	 * writes this object to a save file
	 * 
	 * @param filename the name of the file to be written to
	 * @throws IOException
	 */
	public void writeObject(String filename) throws IOException {
		try {
		    out = new ObjectOutputStream(new FileOutputStream(filename + ".dat"));
			out.writeObject(this);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			out.close();
		}
	}

	/**
	 * sets the list of sellable items
	 * 
	 * @param sellMap the new sell map to be set
	 */
	public void setSellMap(Map<TradeGood, InventoryItem> sellMap) {
	/**
	 * Setter for the map of items that can be sold
	 * @param sellMap the map to be set
	 */
		this.sellMap=sellMap;
	}
	

}
